﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading;
using Netbits.Common.Collections;
using Netbits.Common.Conditions;
using Netbits.Common.Threading;

namespace Netbits.Common.Databases.MemoryTree
{
	/// <summary>
	/// Represents a node in the memory-tree
	/// </summary>
	public sealed class MemoryTreeNode : IEnumerable<MemoryTreeNode>, IDisposable
	{
		#region Private Variables

		Guid _Id = Guid.Empty;
		string _Name = string.Empty;
		MemoryTreeDb _Database;
		MemoryTreeNode _Parent;
		bool _Disposed;

		Lock _Lock;
		readonly LockedKeyedCollection<Guid, MemoryTreeNode> _Nodes;
		readonly LockedKeyedCollection<string, MemoryTreeField> _Fields;

		#endregion

		#region Constructor

		internal MemoryTreeNode( MemoryTreeDb Database_, MemoryTreeNode Parent_ )
		{
			Condition.Requires( Database_, "Database_" ).IsNotNull();
			Condition.Requires( Parent_, "Parent_" ).IsNotNull();

			_Id = Guid.NewGuid();
			_Database = Database_;
			_Parent = Parent_;
			_Lock = new Lock();
			_Nodes = new LockedKeyedCollection<Guid, MemoryTreeNode>( item => item._Id, _Lock );
			_Fields = new LockedKeyedCollection<string, MemoryTreeField>( item => item.Name, _Lock );
		}

		#endregion

		#region Public Properties

		/// <summary>
		/// Returns the owner database
		/// </summary>
		public MemoryTreeDb Database
		{
			get
			{
				return _Database;
			}
		}

		/// <summary>
		/// Returns enumerable field-list
		/// </summary>
		public IEnumerable<MemoryTreeField> Fields
		{
			get
			{
				return _Fields;
			}
		}

		/// <summary>
		/// Returns enumerable node-list
		/// </summary>
		public IEnumerable<MemoryTreeNode> Nodes
		{
			get
			{
				return _Nodes;
			}
		}

		/// <summary>
		/// Returns the parent node. If this is null this is the root node.
		/// </summary>
		public MemoryTreeNode Parent
		{
			get
			{
				return _Parent;
			}
		}

		/// <summary>
		/// Returns the unique memory-tree-node id
		/// </summary>
		public Guid Id
		{
			get
			{
				return _Id;
			}
			private set
			{
				_Id = value;
				SetField( "mt-id", _Id );
			}
		}

		/// <summary>
		/// Return the name of this memory-tree-node or set this. 
		/// </summary>
		public string Name
		{
			get
			{
				return _Name;
			}
			set
			{
				Interlocked.Exchange( ref _Name, value );
			}
		}

		/// <summary>
		/// Returns true if this node is the root-node
		/// </summary>
		public bool IsRootNode
		{
			get
			{
				return (_Parent == null);
			}
		}

		#endregion

		#region Public Functions

		/// <summary>
		/// Copy from specified Node 
		/// </summary>
		/// <param name="Node_">Node to copy from</param>
		public void CopyFrom( MemoryTreeNode Node_ )
		{
			CopyFrom( Node_, -1 );
		}

		/// <summary>
		/// Copy from specified Node
		/// </summary>
		/// <param name="Node_">Node to copy from</param>
		/// <param name="Death_">How many nodes to copy</param>
		public void CopyFrom( MemoryTreeNode Node_, int Death_ )
		{
			Condition.Requires( Node_, "Node_" ).IsNotNull();
			
			Id = Node_.Id;
			foreach( MemoryTreeField sdf in Node_.Fields )
			{
				if( sdf.Name == "mt-id" )
					continue;

				_Fields.Add( sdf.Clone() );
			}

			if( Death_ == 0 )
				return;

			foreach( MemoryTreeNode sdnChild in Node_ )
			{
				MemoryTreeNode sdnNew = Add( sdnChild.Name );
				sdnNew.CopyFrom( sdnChild, Death_ - 1 );
			}
		}

		/// <summary>
		/// Add new node
		/// </summary>
		/// <returns>Returns the new node.</returns>
		public MemoryTreeNode Add()
		{
			return Add( string.Empty );
		}

		/// <summary>
		/// Add new node with specified name
		/// </summary>
		/// <param name="Name_">Name of the new node</param>
		/// <returns>Returns the new node.</returns>
		public MemoryTreeNode Add( string Name_ )
		{
			Condition.Requires( Name_, "Name_" ).IsNotNull();

			MemoryTreeNode mtn = new MemoryTreeNode( _Database, this );
			mtn.Name = Name_;
			mtn.SetField( "mt-id", mtn.Id );

			_Nodes.Add( mtn );
			_Database.__AddNode( mtn );
			return mtn;
		}

		/// <summary>
		/// Remove this node from the database.
		/// </summary>
		public void Remove()
		{
			if( _Parent == null )
				throw new InvalidOperationException( "you can't remove the root-node" );

			_Parent._Nodes.Remove( this );
			_Database.__RemoveNode( this );

			this.Dispose();
		}

		/// <summary>
		/// Search for the specified field-name 
		/// </summary>
		/// <param name="Name_">Field-name to search</param>
		/// <returns>MemoryTreeField or null</returns>
		public MemoryTreeField FindField( string Name_ )
		{
			Condition.Requires( Name_, "Name_" ).IsNotNull().IsNotEmpty();
			return _Fields[Name_];
		}

		/// <summary>
		/// Search for the specified node-name
		/// Not recursive and no case compare
		/// </summary>
		/// <param name="Name_">Name of the node to search</param>
		/// <returns>MemoryTreeNode or null</returns>
		public MemoryTreeNode FindNode( string Name_ )
		{
			return FindNode( Name_, false, false );
		}

		/// <summary>
		/// Search for the specified node-name with no recursion 
		/// </summary>
		/// <param name="Name_">Name of the node to search</param>
		/// <param name="Recursiv_">True if we must search recursive</param>
		/// <returns>The found memory-tree-node otherwise null</returns>
		public MemoryTreeNode FindNode( string Name_, bool Recursiv_ )
		{
			return FindNode( Name_, Recursiv_, false );
		}

		/// <summary>
		/// Search for the specified node-name
		/// </summary>
		/// <param name="Name_">Name of the node to find</param>
		/// <param name="Recursiv_">True if we must search recursive</param>
		/// <param name="IgnoreCase_">True if we ignore case</param>
		/// <returns>The found memory-tree-node otherwise null</returns>
		public MemoryTreeNode FindNode( string Name_, bool Recursiv_, bool IgnoreCase_ )
		{
			Condition.Requires( Name_, "Name_" ).IsNotNull().IsNotEmpty();

			foreach( MemoryTreeNode sdn in _Nodes )
			{
				if( string.Compare( sdn.Name, Name_, IgnoreCase_ ) == 0 )
					return sdn;

				if( Recursiv_ )
				{
					MemoryTreeNode sdnFound = sdn.FindNode( Name_, Recursiv_, IgnoreCase_ );
					if( sdnFound != null )
						return sdnFound;
				}
			}

			return null;
		}

		/// <summary>
		/// Search for the specified node-id with no recursion
		/// </summary>
		/// <param name="Id_">ID of the node to find</param>
		/// <returns>The found memory-tree-node otherwise null</returns>
		public MemoryTreeNode FindNode( Guid Id_ )
		{
			return FindNode( Id_, false );
		}

		/// <summary>
		/// Search for the specified node-id with specified recursion
		/// </summary>
		/// <param name="Id_">ID of the node to find</param>
		/// <param name="Recursiv_">True if we must search recursive</param>
		/// <returns>The found memory-tree-node otherwise null</returns>
		public MemoryTreeNode FindNode( Guid Id_, bool Recursiv_ )
		{
			Condition.Requires( Id_, "Id_" ).IsNotEmpty();

			MemoryTreeNode sdn = _Nodes[Id_];
			if( sdn != null )
				return sdn;

			if( Recursiv_ )
			{
				foreach( MemoryTreeNode sdnr in _Nodes )
				{
					MemoryTreeNode sdnFound = sdnr.FindNode( Id_, Recursiv_ );
					if( sdnFound != null )
						return sdnFound;
				}
			}
			return null;
		}

		/// <summary>
		/// Find the node with the specified field-name with value.
		/// No recursion and compare case
		/// </summary>
		/// <param name="Name_">Name of the field to search</param>
		/// <param name="Value_">Value of the field to search</param>
		/// <returns>The found memory-tree-node otherwise null</returns>
		public MemoryTreeNode FindNode( string Name_, object Value_ )
		{
			return FindNode( Name_, Value_, false );
		}

		/// <summary>
		/// Find the node with the specified field-name with value.
		/// </summary>
		/// <param name="Name_">Name of the field to search</param>
		/// <param name="Value_">Value of the field to search</param>
		/// <param name="Recursiv_">True if we must search recursive</param>
		/// <returns>The found memory-tree-node otherwise null</returns>
		public MemoryTreeNode FindNode( string Name_, object Value_, bool Recursiv_ )
		{
			return FindNode( Name_, Value_, Recursiv_, false );
		}

		/// <summary>
		/// Find the node with the specified field-name with value.
		/// </summary>
		/// <param name="Name_">Name of the field to search</param>
		/// <param name="Value_">Value of the field to search</param>
		/// <param name="Recursiv_">True if we must search recursive</param>
		/// <param name="IgnoreCase_">True if we must ignore case</param>
		/// <returns>The found memory-tree-node otherwise null</returns>
		public MemoryTreeNode FindNode( string Name_, object Value_, bool Recursiv_, bool IgnoreCase_ )
		{
			Condition.Requires( Name_, "Name_" ).IsNotNull().IsNotEmpty();
			Condition.Requires( Value_, "Value_" ).IsNotNull();

			MemoryTreeField sdf = _Fields[Name_];
			if( sdf != null )
			{
				Type t = Value_.GetType();
				if( t == typeof( bool ) )
				{
					if( sdf.Type == MemoryTreeFieldType.Bool && (bool)Value_ == sdf.GetValue<bool>() )
						return this;
				}
				else if( t == typeof( sbyte ) )
				{
					if( sdf.Type == MemoryTreeFieldType.Byte && (sbyte)Value_ == sdf.GetValue<sbyte>() )
						return this;
				}
				else if( t == typeof( DateTime ) )
				{
					if( sdf.Type == MemoryTreeFieldType.DateTime && ((DateTime)Value_).Ticks == sdf.GetValue<DateTime>().Ticks )
						return this;
				}
				else if( t == typeof( decimal ) )
				{
					if( sdf.Type == MemoryTreeFieldType.Decimal && (decimal)Value_ == sdf.GetValue<decimal>() )
						return this;
				}
				else if( t == typeof( double ) )
				{
					if( sdf.Type == MemoryTreeFieldType.Double && (double)Value_ == sdf.GetValue<double>() )
						return this;
				}
				else if( t == typeof( float ) )
				{
					if( sdf.Type == MemoryTreeFieldType.Float && (float)Value_ == sdf.GetValue<float>() )
						return this;
				}
				else if( t == typeof( int ) )
				{
					if( sdf.Type == MemoryTreeFieldType.Int && (int)Value_ == sdf.GetValue<int>() )
						return this;
				}
				else if( t == typeof( IPAddress ) )
				{
					if( sdf.Type == MemoryTreeFieldType.IPAddress && ((IPAddress)Value_).ToString() == sdf.GetValue<IPAddress>().ToString() )
						return this;
				}
				else if( t == typeof( long ) )
				{
					if( sdf.Type == MemoryTreeFieldType.Long && (long)Value_ == sdf.GetValue<long>() )
						return this;
				}
				else if( t == typeof( short ) )
				{
					if( sdf.Type == MemoryTreeFieldType.Short && (short)Value_ == sdf.GetValue<short>() )
						return this;
				}
				else if( t == typeof( string ) )
				{
					if( sdf.Type == MemoryTreeFieldType.String && (string)Value_ == sdf.GetValue<string>() )
						return this;
				}
				else if( t == typeof( TimeSpan ) )
				{
					if( sdf.Type == MemoryTreeFieldType.Timespan && ((TimeSpan)Value_).Ticks == sdf.GetValue<TimeSpan>().Ticks )
						return this;
				}
				else if( t == typeof( byte ) )
				{
					if( sdf.Type == MemoryTreeFieldType.UByte && (byte)Value_ == sdf.GetValue<byte>() )
						return this;
				}
				else if( t == typeof( uint ) )
				{
					if( sdf.Type == MemoryTreeFieldType.UInt && (uint)Value_ == sdf.GetValue<uint>() )
						return this;
				}
				else if( t == typeof( ulong ) )
				{
					if( sdf.Type == MemoryTreeFieldType.ULong && (ulong)Value_ == sdf.GetValue<ulong>() )
						return this;
				}
				else if( t == typeof( ushort ) )
				{
					if( sdf.Type == MemoryTreeFieldType.UShort && (ushort)Value_ == sdf.GetValue<ushort>() )
						return this;
				}
				else if( t == typeof( Guid ) )
				{
					if( sdf.Type == MemoryTreeFieldType.Guid && ((Guid)Value_).ToString() == sdf.GetValue<Guid>().ToString() )
						return this;
				}
			}

			if( Recursiv_ )
			{
				foreach( MemoryTreeNode sdn in _Nodes )
				{
					MemoryTreeNode sdnFound = sdn.FindNode( Name_, Value_, Recursiv_, IgnoreCase_ );
					if( sdnFound != null )
						return sdnFound;
				}
			}

			return null;
		}

		/// <summary>
		/// Set the field value.
		/// If the field not exists it shall create
		/// </summary>
		/// <param name="Name_">Name of the field to set/create</param>
		/// <param name="Value_">Value of the field</param>
		public void SetField( string Name_, object Value_ )
		{
			Condition.Requires( Name_, "Name_" ).IsNotNull().IsNotEmpty();
			Condition.Requires( Value_, "Value_" ).IsNotNull();

			MemoryTreeField sdf = null;
			using( _Lock.Write() )
			{
				sdf = _Fields[Name_];
				if( sdf == null )
				{
					sdf = new MemoryTreeField( Name_ );
					_Fields.Add( sdf );
				}
			}

			sdf.SetValue( Value_ );
			_Database.__SetMustSave();
		}

		/// <summary>
		/// Remove the specified field
		/// </summary>
		/// <param name="Name_">Name of the field to remove</param>
		public void RemoveField( string Name_ )
		{
			Condition.Requires( Name_, "Name_" ).IsNotNull().IsNotEmpty();

			_Fields.Remove( Name_ );
			_Database.__SetMustSave();
		}

		/// <summary>
		/// Get the typed field-value.
		/// </summary>
		/// <typeparam name="TValue">Type of the field-value</typeparam>
		/// <param name="Name_">Name of the field</param>
		/// <returns>The typed value otherwise specified exception</returns>
		public TValue GetField<TValue>( string Name_ )
		{
			Condition.Requires( Name_, "Name_" ).IsNotNull().IsNotEmpty();

			MemoryTreeField sdf = _Fields[Name_];
			if( sdf == null )
				throw new Exception( "field not found!" );

			return sdf.GetValue<TValue>();
		}

		/// <summary>
		/// Get the typed field-value.
		/// </summary>
		/// <typeparam name="TValue">Type of the field-value</typeparam>
		/// <param name="Name_">Name of the field</param>
		/// <param name="Default_">Default return value</param>
		/// <returns>The typed value otherwise Default_</returns>
		public TValue GetField<TValue>( string Name_, TValue Default_ )
		{
			try
			{
				Condition.Requires( Name_, "Name_" ).IsNotNull().IsNotEmpty();

				MemoryTreeField sdf = _Fields[Name_];
				if( sdf == null )
					return Default_;

				return sdf.GetValue<TValue>();
			}
			catch
			{
				return Default_;
			}
		}

		/// <summary>
		/// Create a string represent this node with specified levels.
		/// </summary>
		/// <param name="Levels_">Amount of levels to include</param>
		/// <returns>The represent string for this node</returns>
		public string ToString( int Levels_ )
		{
			StringBuilder sb = new StringBuilder();
			__ToString( sb, Levels_ );
			return sb.ToString();
		}

		#endregion

		#region Internal Functions

		internal int __FromText( string Text_, int Index_ )
		{
			bool startNodeFound = false;
			int index = Index_;
			int length = Text_.Length;
			
			StringBuilder sbName = new StringBuilder();
			while( index < length )
			{
				char c = Text_[index++];
				switch( c )
				{
					case '[':
						if( !startNodeFound )
						{
							startNodeFound = true;
							continue;
						}
						else
						{
							--index;
							MemoryTreeNode sdn = new MemoryTreeNode( _Database, this );
							index = sdn.__FromText( Text_, index );
							_Nodes.Add( sdn );
							_Database.__AddNode( sdn );
						}
						break;

					case '{':
						--index;
						MemoryTreeField sdf = new MemoryTreeField( string.Empty );
						index = sdf.FromText( Text_, index);
						_Fields.Add( sdf );
						break;

					case ']':
						if( !startNodeFound )
							throw new Exception( "invalid node state. start-state not found" );

						_Name = sbName.ToString();
						_Id = GetField<Guid>( "mt-id", Guid.NewGuid() );
						return index;
					default:
						sbName.Append( c );
						break;
				}
			}
			throw new InvalidOperationException( "invalid node state. no valid simple-db-file" );
		}

		internal void __ToString( StringBuilder Builder_, int Levels_ )
		{
			Builder_.Append( '[' );
			try
			{
				_Lock.EnterReadLock();
				Builder_.Append( _Name );
				foreach( MemoryTreeField sdf in _Fields )
					sdf.ToStringBuilder( Builder_ );

				if( Levels_ != 0 )
				{
					foreach( MemoryTreeNode sdn in _Nodes )
						sdn.__ToString( Builder_, Levels_ - 1 );
				}
			}
			finally
			{
				_Lock.ExitReadLock();
			}
			Builder_.Append( ']' );
		}

		#endregion	

		#region Overrides

		/// <summary>
		/// Returns the hash-code for this node-instance.
		/// </summary>
		/// <returns>hash for this node-instance</returns>
		public override int GetHashCode()
		{
			return this.Id.GetHashCode();
		}

		/// <summary>
		/// Returns the string that representable this memory-tree-node
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return ToString( -1 );
		}

		#endregion

		#region IDisposable

		/// <summary>
		/// Dispose all resources 
		/// </summary>
		public void Dispose()
		{
			if( !_Disposed )
			{
				_Id = Guid.Empty;
				_Name = string.Empty;

				foreach( MemoryTreeNode mtn in _Nodes )
					mtn.Dispose();
				_Nodes.Clear();

				foreach( MemoryTreeField mtf in _Fields )
					mtf.Dispose();
				_Fields.Clear();

				if( _Lock != null )
				{
					_Lock.Dispose();
					_Lock = null;
				}

				_Disposed = true;
			}

			GC.SuppressFinalize( this );
		}

		#endregion

		#region IEnumerator

		/// <summary>
		/// Returns a locked enumerator that iterates through the memory-tree-nodes
		/// </summary>
		/// <returns>The new instance of locked-enumerator</returns>
		public IEnumerator<MemoryTreeNode> GetEnumerator()
		{
			return _Nodes.GetEnumerator();
		}

		/// <summary>
		/// Returns a locked enumerator that iterates through the memory-tree-nodes
		/// </summary>
		/// <returns>The new instance of locked-enumerator</returns>
		System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
		{
			return _Nodes.GetEnumerator();
		}

		#endregion
	}
}
